package com.wangzh.king.utils;

import com.wangzh.king.Exception.KingClassException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 扫描类相关工具类
 * User: wangzh
 * Date: 2016/8/24
 * Time: 14:01
 */
public class ClassUtils {

    private static Logger logger = LoggerFactory.getLogger(ClassUtils.class);


    /**
     * 私有化构造器
     */
    private ClassUtils(){}

    /**
     * 获得所在线程类加载器
     * @return
     */
    private static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 加载class
     * @param className
     * @return
     */
    private static Class<?> loadClass(String className,boolean ininialize){
        Class<?> obj = null;
        try {
          obj =  Class.forName(className,ininialize,getClassLoader());
        } catch (ClassNotFoundException e) {
            logger.error("加载["+className+"]出错",e);
            new KingClassException("加载["+className+"]出错",e);
        }
        return obj;
    }

    /**
     * 通过包名获得类
     * @param packageName
     */
    public static Set<Class<?>> getClassByPackageName(String packageName){
        Set<Class<?>> set = new HashSet<Class<?>>();
        //替换包路径
        String packageDir = packageName.replace(".","/");
        //循环处理目录
        Enumeration<URL> dirs;
        try {
            dirs = getClassLoader().getResources(packageDir);
            //循环迭代
            while(dirs.hasMoreElements()){
                URL url = dirs.nextElement();
                //获得协议的名称
                String protocol = url.getProtocol();
                //文件格式
                if("file".equals(protocol)){
                    //获得包物理路径
                    String packagePath = url.getPath().replaceAll("%20"," ");
                    addClass(set,packagePath,packageName);
                }else if("jar".equals(protocol)){
                    //解析 jar 包中的 entity
                    JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                    JarFile jarFile = jarURLConnection.getJarFile();
                    Enumeration<JarEntry> jarEntries = jarFile.entries();
                    while(jarEntries.hasMoreElements()){
                        JarEntry jarEntry = jarEntries.nextElement();
                        String jarEntryName = jarEntry.getName();
                        //判断是否为 class 文件
                        if(jarEntryName.endsWith(".class")){
                            String className = jarEntryName.substring(0,jarEntryName.lastIndexOf(".")).replaceAll("/",".");
                            doAddClass(set,className);
                        }
                    }
                }
            }
        } catch (IOException e) {
            //TODO 异常日志处理
            e.printStackTrace();
        }

        return set;
    }


    private static void addClass(Set<Class<?>> classSet,String packagePath,String packageName){
        //获得包路径下的文件或者目录
        File[] files  = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });

        //遍历
        for(File file : files){
            String fileName = file.getName();
            //判断是否文件
            if(file.isFile()){
                //获得类名
                String className = fileName.substring(0,fileName.lastIndexOf("."));
                className = packageName + "." + className;
                //执行添加
                doAddClass(classSet,className);
             }else{
                //获取子包
                String subPackagePath = fileName;
                subPackagePath = packagePath + "/" + subPackagePath;

                //获取子包
                String subPackageName = fileName;
                subPackageName = packageName + "/" + subPackageName;

                //递归调用
                addClass(classSet,subPackagePath,subPackageName);
             }
        }
    }

    private static void doAddClass(Set<Class<?>> classSet,String className){
        //加载类
        Class<?> cls = loadClass(className,false);
        classSet.add(cls);
    }


}
